﻿


using IotClientService.Operations.plc;
using IotContract.Dtos.Clients;
using IotContract.Dtos.Clients.Equips;
using IotContract.Dtos.Servers;
using IotContract.Dtos.Servers.Pushs;
using IotContract.Enums;
using IotContract.Interfaces;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XT.Common.Enums;
using XT.Common.Extensions;
using XT.Common.Models.SignalR;
using XT.Common.Services;

namespace IotClientService.Managers;
/// <summary>
/// 抽象设备管理服务
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class EquipBaseManager<T,K>: IEquipManager where T:IotEquipDto where K : PushEquipDto
{
    private readonly ILogService _logService;
   
    private readonly IConnectDataService _connectDataService;

    private readonly IServiceProvider _provider;

   
    public virtual  ConcurrentDictionary<long, IEquipOperation<T>> OperationDic { get; set; } = new ConcurrentDictionary<long, IEquipOperation<T>>();

    public EquipBaseManager(ILogService logService, IConnectDataService connectDataService,IServiceProvider provider) { _logService = logService; _connectDataService = connectDataService;_provider = provider; }

    /// <summary>
    /// 协议类型
    /// </summary>
    public abstract string ProtobufType { get; set; }
    public virtual bool HasTags { get; set; } = false;

    /// <summary>
    /// 推送
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async virtual Task<bool> PushAndWriteEquips()
    {
        try
        {


            List<K> pushs = new List<K>();
            foreach (var operation in OperationDic.Values)
            {
                if (!operation.GetConnection()) continue;
                var equip = operation.GetEquipInfo();
                if (equip == null) continue;
                var push = equip.Adapt<K>();
                push.Connection = operation.GetConnection();
                push.Status = operation.GetOperationStatus();
                push.HasTags = HasTags;
                pushs.Add(push);
            }
            if(pushs.Count==0) return false;
            var writeDatas =await _connectDataService.PushEquips(pushs);

            foreach (var data in writeDatas)
            {
                if (data == null) continue;
                OperationDic.TryGetValue(data.EquipId, out var writeOperation);
                if (writeOperation == null)
                {
                    continue;
                }
                await writeOperation.WriteRequest(data);

            }

            return true;
        }
        catch(Exception ex)
        {
            _logService.LogError($"{ProtobufType} push error,{ex.Message}");
            return false;
        }
    }
    /// <summary>
    /// 推送单个设备
    /// </summary>
    /// <param name="operation"></param>
    /// <returns></returns>
    public async Task PushAndWriteEquip(IEquipOperation<T> operation)
    {
        try
        {
            List<K> pushs = new List<K>();
            var equip = operation.GetEquipInfo();
            if (equip == null) return;
            var push = equip.Adapt<K>();
            push.Connection = operation.GetConnection();
            push.Status = operation.GetOperationStatus();
            push.HasTags = HasTags;
            pushs.Add(push);
            var writeDatas = await _connectDataService.PushEquips(pushs);
            foreach (var data in writeDatas)
            {
                if (data == null) continue;
                OperationDic.TryGetValue(data.EquipId, out var writeOperation);
                if (writeOperation == null)
                {
                    continue;
                }
                await writeOperation.WriteRequest(data);

            }
        }
        catch(Exception ex)
        {
            _logService.LogError($"{ProtobufType} push error,{ex.Message}");
           
        }
    }


    public virtual async Task<bool> Run(List<IotEquipDto> equips)
    {
        if (equips.Count == 0) return true;
        var tags = new List<EquipTagDto>();
        if (HasTags)
        {


            // 查询点位信息
            var tagsMsg = await _connectDataService.GetEquipTags(new IotContract.Models.Clients.ClientRequestCommand
            {
                ProgramId = equips[0].ProgramId,
                RequestType = ProtobufType
            });
            if (tagsMsg.Item2.IsNotNullOrEmpty())
            {
                // 添加远程日志
                await _logService.LogRemote(new RemoteLog
                {
                    Title =ProtobufType,
                    ID= ((IotProgramType)equips[0].ProgramType).ToString(),                    
                    IsToDB = true,
                    IsToRemote = true,
                    Type = LogEnum.Error,
                    Content = $"{equips[0].ProgramId} {ProtobufType} read equips tags error, {tagsMsg.Item2}"
                });
                return false;
            }
            tags = tagsMsg.Item1;
        }


        // 添加远程日志
        await _logService.LogRemote(new RemoteLog
        {
            Title = ProtobufType,
            ID= ((IotProgramType)equips[0].ProgramType).ToString(),
            IsToDB = false,
            IsToRemote = true,
            Type = LogEnum.Info,
            Content = $"{ProtobufType} read equips count {equips.Count}"
        });

        List<IEquipOperation<T>> operations = new List<IEquipOperation<T>>();

        // 先初始化
        foreach (var equip in equips)
        {
            if (OperationDic.ContainsKey(equip.Id))
            {
                await OperationDic[equip.Id].OperationStop();
                OperationDic.TryRemove(equip.Id, out _);
            }
            var opera = _provider.GetRequiredService<IEquipOperation<T>>();
            
            OperationDic.TryAdd(equip.Id, opera);
            opera.OnConnectStatusChanged += async (o, e) =>
            {
                var operation = o as IEquipOperation<T>;
                if (operation == null) return;
                await PushAndWriteEquip(operation);
                var equipment = operation.GetEquipInfo();
                var log = $"{equipment.ToString()} {e.Message}";


                if (!e.IsSuccess)
                {
                    await _logService.LogRemote(new RemoteLog
                    {
                        ID = ((IotProgramType)equipment.ProgramType).ToString(),
                        Title = equipment.EquipType,
                        IsToDB = true,
                        IsToRemote = true,
                        Type = LogEnum.Error,
                        Content = log,
                        Flag = equipment.Num
                    });
                }

            };
            var result = await opera.Init(equip.Adapt<T>(),tags.Where(x => x.EquipInfoId == equip.Id));
            if (!result)
            {
                // 添加远程日志
                await _logService.LogRemote(new RemoteLog
                {
                    Title = equip.EquipType,
                    ID= ((IotProgramType)equip.ProgramType).ToString(),
                    IsToDB = false,
                    IsToRemote = true,
                    Flag=equip.Num,
                    Type = LogEnum.Info,
                    Content = $"{equip.ToString()} create operation error"
                });
                continue;
            }
            

            operations.Add(opera);
        }

        // 再启动
        foreach (var opera in operations)
        {
            await opera.OperationStart();
        }

        return true;
    }
    /// <summary>
    /// 启动单个设备
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    public virtual async Task<bool> Run(IotEquipDto equip)
    {
       
        var tags = new List<EquipTagDto>();
        if (HasTags)
        {


            // 查询点位信息
            var tagsMsg = await _connectDataService.GetEquipTags(new IotContract.Models.Clients.ClientRequestCommand
            {
                ProgramId = equip.ProgramId,
                EquipId=equip.Id,
                RequestType = ProtobufType
            });
            if (tagsMsg.Item2.IsNotNullOrEmpty())
            {
                // 添加远程日志
                await _logService.LogRemote(new RemoteLog
                {
                    ID = ((IotProgramType)equip.ProgramType).ToString(),
                    Title = equip.EquipType,
                    Flag=equip.Num,
                    IsToDB = true,
                    IsToRemote = true,                  
                    Type = LogEnum.Error,
                    Content = $"{equip.ToString()} read equip tags error, {tagsMsg.Item2}"
                });
                return false;
            }
            tags = tagsMsg.Item1;
        }


        // 添加远程日志
        await _logService.LogRemote(new RemoteLog
        {
            ID = ((IotProgramType)equip.ProgramType).ToString(),
            Title = equip.EquipType,
            Flag=equip.Num,
            IsToDB = false,
            IsToRemote = true,
            Type = LogEnum.Info,
            Content = $"{equip.ToString()} single equip run"
        });

        List<IEquipOperation<T>> operations = new List<IEquipOperation<T>>();

        // 先初始化
        
        if (OperationDic.ContainsKey(equip.Id))
        {
            await OperationDic[equip.Id].OperationStop();
            OperationDic.TryRemove(equip.Id, out _);
        }

        var opera = _provider.GetRequiredService<IEquipOperation<T>>();
       
        OperationDic.TryAdd(equip.Id, opera);
        var result = await opera.Init(equip.Adapt<T>(), tags.Where(x => x.EquipInfoId == equip.Id));
        if (!result)
        {
            // 添加远程日志
            await _logService.LogRemote(new RemoteLog
            {
                ID = ((IotProgramType)equip.ProgramType).ToString(),
                Title = equip.EquipType,
                Flag =equip.Num,
                IsToDB = true,
                IsToRemote = true,
                Type = LogEnum.Info,
                Content = $"{equip.ToString()} create operation error"
            });
            return false;
        }
        opera.OnConnectStatusChanged += async (o, e) =>
        {
            var log = $"{equip.ToString()} ConnetStatus {e.Connect},IsSuccess {e.IsSuccess},Message,{e.Message}";


            if (!e.IsSuccess)
            {
                await _logService.LogRemote(new RemoteLog
                {
                    ID = ((IotProgramType)equip.ProgramType).ToString(),
                    Title = opera.GetEquipInfo().EquipType,
                    IsToDB = true,
                    IsToRemote = true,
                    Type = LogEnum.Error,
                    Content = log,
                    Flag = equip.Num
                });
            }

        };

        operations.Add(opera);
        

       
        await opera.OperationStart();
        

        return true;
    }
    /// <summary>
    /// 停止所有设备
    /// </summary>
    /// <returns></returns>
    public virtual async Task<bool> Stop()
    {
        if (OperationDic.Count == 0) return true;
        foreach (var opera in OperationDic.Values)
        {
            await opera.OperationStop();
        }

        var programType = OperationDic[0].GetEquipInfo().ProgramType;

        await _logService.LogRemote(new RemoteLog
        {
            ID = ((IotProgramType)programType).ToString(),
            Title = ProtobufType,
            IsToDB = true,
            IsToRemote = true,
            Type = LogEnum.Info,
            Content = $"Stop All Equips"
        });
        return true;
    }
    /// <summary>
    /// 停止相关设备,不移除
    /// </summary>
    /// <param name="equipIds"></param>
    /// <returns></returns>
    public virtual async Task<bool> Stop(List<long> equipIds)
    {
        foreach (var id in equipIds)
        {
            var result = OperationDic.TryGetValue(id, out var opera);

            if (result && opera!=null)
            {
                // 停止
                await opera.OperationStop();
               
            }
        }
        return true;
    }
    /// <summary>
    /// 停止程序的所有设备
    /// </summary>
    /// <param name="programId"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<bool> StopProgram(long programId)
    {
        if (OperationDic.Count == 0)
        {
            return true;
        }
        foreach(var opera in OperationDic.Values)
        {
            if(opera.GetEquipInfo().ProgramId== programId)
            {
                await opera.OperationStop();
            }
        }
        var programType = OperationDic[0].GetEquipInfo().ProgramType;
        // 添加远程日志
        await _logService.LogRemote(new RemoteLog
        {
            ID = ((IotProgramType)programType).ToString(),
            Title = ProtobufType,
            IsToDB = true,
            IsToRemote = true,
            Type = LogEnum.Info,
            Content = $"program stop all equips,{programId}"
        });
        return true;
    }

    /// <summary>
    /// 清理离线的设备
    /// </summary>
    /// <param name="programId"></param>
    /// <returns></returns>
    public async Task<bool> ClearOffline(long programId)
    {
        if (OperationDic.Count == 0)
        {
            return true;
        }
        List<long> equipIds = new List<long>();
        foreach (var opera in OperationDic.Values)
        {
            if (opera.GetEquipInfo().ProgramId == programId && !opera.GetConnection())
            {
                await opera.OperationStop();
                equipIds.Add(opera.GetEquipInfo().Id);
            }
        }
        
        // 5s后移除设备
        Task.Factory.StartNew(async () =>
        {
            var ids = equipIds;
            await Task.Delay(5000);

            foreach(var id in ids)
            {
                OperationDic.TryRemove(id, out _);
            }

        });

        var programType = OperationDic[0].GetEquipInfo().ProgramType;

        // 添加远程日志
        await _logService.LogRemote(new RemoteLog
        {
            Title = ProtobufType,
            ID= ((IotProgramType)programType).ToString(),
            IsToDB = true,
            IsToRemote = true,
            Type = LogEnum.Info,
            Content = $"program clear all offline equips,{programId}"
        });
        return true;


    }
}
